package com.sky.sd.oauth.translator;

import com.sky.sd.common.web.response.Result;
import com.sky.sd.common.web.response.ResultType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer;
import org.springframework.security.oauth2.common.exceptions.*;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.stereotype.Component;
import org.springframework.web.HttpRequestMethodNotSupportedException;

import java.io.IOException;

/**
 * <br/>功能:
 * <br/>版本: 1.0
 * <br/>开发人员: 弓振
 * <br/>创建日期: 2020/6/17 16:05
 * <br/>修改日期: 2020/6/17 16:05
 * <br/>修改列表:
 */
@Component
@Slf4j
public class SkyWebResponseExceptionTranslator implements WebResponseExceptionTranslator<Result> {

    private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();

    @Override
    public ResponseEntity<Result> translate(Exception e) throws Exception {

        /**
         * 获取异常调用栈，例如
         * RuntimeException r = new RuntimeException();
         * ServiceException serviceException = new ServiceException(r);
         * ControllerException controllerException = new ControllerException(serviceException);
         * Throwable[] throwables = throwableAnalyzer.determineCauseChain(controllerException);
         *
         * 得到的结果为
         * com.sky.sd.core.exception.controller.ControllerException: com.sky.sd.core.exception.service.ServiceException: java.lang.RuntimeException
         * com.sky.sd.core.exception.service.ServiceException: java.lang.RuntimeException
         * java.lang.RuntimeException
         */
        Throwable[] causeChain = throwableAnalyzer.determineCauseChain(e);
        // oauth异常
        Exception ase = (OAuth2Exception) throwableAnalyzer.getFirstThrowableOfType(OAuth2Exception.class, causeChain);
        if(ase != null) {
            return handleOAuth2Exception((OAuth2Exception) ase);
        }

        // 认证异常
        ase = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class,
                causeChain);
        if (ase != null) {
            if(ase instanceof InsufficientAuthenticationException) {
                return handleOAuth2Exception(new UnauthorizedException("client认证异常", ase));
            } else {
                return handleOAuth2Exception(new UnauthorizedException(ase.getMessage(), ase));
            }
        }

        // 授权失败
        ase = (AccessDeniedException) throwableAnalyzer
                .getFirstThrowableOfType(AccessDeniedException.class, causeChain);
        if (ase instanceof AccessDeniedException) {
            return handleOAuth2Exception(new ForbiddenException(ase.getMessage(), ase));
        }

        // 请求方法不支持
        ase = (HttpRequestMethodNotSupportedException) throwableAnalyzer.getFirstThrowableOfType(
                HttpRequestMethodNotSupportedException.class, causeChain);
        if (ase instanceof HttpRequestMethodNotSupportedException) {
            return handleOAuth2Exception(new MethodNotAllowed(ase.getMessage(), ase));
        }

        // 服务器内部错误
        return handleOAuth2Exception(new ServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), e));
    }

    private ResponseEntity<Result> handleOAuth2Exception(OAuth2Exception e) throws IOException {

        log.info("==> 获取token异常: {}",e.getMessage());
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");

        int status = e.getHttpErrorCode();
        String message = e.getMessage();

        ResponseEntity<Result> response = new ResponseEntity<Result>(Result.fail(status,message), headers,
                HttpStatus.valueOf(status));
        return response;
    }

    private static class UnauthorizedException extends OAuth2Exception {

        public UnauthorizedException(String msg, Throwable t) {
            super(msg, t);
        }

        @Override
        public int getHttpErrorCode() {
            return ResultType.UNAUTHORIZED.getCode();
        }

        @Override
        public String getOAuth2ErrorCode() {
            return "unauthorized";
        }
    }

    private static class ForbiddenException extends OAuth2Exception {

        public ForbiddenException(String msg, Throwable t) {
            super(msg, t);
        }

        @Override
        public int getHttpErrorCode() {
            return ResultType.FORBIDDEN.getCode();
        }

        @Override
        public String getOAuth2ErrorCode() {
            return "forbidden";
        }
    }

    private static class MethodNotAllowed extends OAuth2Exception {

        public MethodNotAllowed(String msg, Throwable t) {
            super(ResultType.METHOD_NOT_ALLOWED.getMsg(), t);
        }

        @Override
        public int getHttpErrorCode() {
            return ResultType.METHOD_NOT_ALLOWED.getCode();
        }

        @Override
        public String getOAuth2ErrorCode() {
            return "method_not_allowed";
        }
    }
    private static class ServerErrorException extends OAuth2Exception {

        public ServerErrorException(String msg, Throwable t) {
            super(ResultType.INTERNAL_SERVER_ERROR.getMsg(), t);
        }

        @Override
        public int getHttpErrorCode() {
            return ResultType.INTERNAL_SERVER_ERROR.getCode();
        }

        @Override
        public String getOAuth2ErrorCode() {
            return "method_not_allowed";
        }
    }
}
